
#include <stdarg.h> /* for va_start, va_end */ 

#include "vty_agent_session.hpp"
#include "cmd_agent_mng.hpp"

vty_agent_session::vty_agent_session(io_service& ioserv):
    _id(0), _socket(ioserv), _is_dialog(false), _diag_function(NULL)
{
    PDBG("create vty proxy agent !!!!!\n");
}

vty_agent_session::~vty_agent_session() 
{
    PDBG("delete vty proxy agent !!!!!\n");
}

bool vty_agent_session::recv_ipc(vty_ipc_message_t& msg)
{
    boost::system::error_code error;
    size_t length = _socket.read_some(
            boost::asio::buffer(msg.data(), vty_ipc_message_t::header_length), 
            error);

    if (error == boost::asio::error::eof)
        return false;
    else if (error)
        throw boost::system::system_error(error); // Some other error.

    msg.decode_header();
    length = _socket.read_some(
            boost::asio::buffer(msg.body(), msg.body_length()), 
            error);

    if (error == boost::asio::error::eof)
        return false;
    else if (error)
        throw boost::system::system_error(error); // Some other error.
    return true;
}



bool vty_agent_session::send_ipc(vty_ipc_message_t& msg)
{
    boost::asio::write(_socket, boost::asio::buffer(msg.data(), msg.length()));
    return true;
};

__u32 vty_agent_session::send_vty_msg(__u32 vty_id, __u32 cmd, 
        string_vector& req_opts, string_vector& resp_opts)
{
    __u32 ret = VTY_SUCC;
    vty_ipc_message_t msg;

    if (cmd == VTY_START_DIAG)
        PDBG(">> SEND VTY_START_DIAG, ret(%d)\n", ret);

    _ipcmsg.build_msg(vty_id, cmd, VTY_RET_NONE, req_opts, msg);
    if (send_ipc(msg) == false) return 0xFF;    /* FIXME */
    if (recv_ipc(msg) == false ) return 0xFF;

    _ipcmsg.parse_msg(msg, vty_id, cmd, ret, resp_opts);
    if (cmd == VTY_START_DIAG)
        PDBG(">> RECV VTY_START_DIAG, ret(%d)\n", ret);

    return ret;
}

bool vty_agent_session::is_auth_mode() 
{
    __u32 ret = VTY_SUCC;
    string_vector req_opts, resp_opts;

    ret = send_vty_msg(id(), VTY_IS_AUTH_MODE, req_opts, resp_opts);
    if (ret != VTY_SUCC) return false;
    return true;
}

bool vty_agent_session::is_auth_mode(const std::string& mode)
{
    __u32 ret = VTY_SUCC;
    string_vector req_opts, resp_opts;

    req_opts.push_back(mode);
    ret = send_vty_msg(id(), VTY_IS_AUTH_MODE, req_opts, resp_opts);
    if (ret != VTY_SUCC) return false;
    return true;
}

bool vty_agent_session::check_passwd(const std::string passwd)
{
    __u32 ret = VTY_SUCC;
    string_vector req_opts, resp_opts;

    req_opts.push_back(passwd);
    ret = send_vty_msg(id(), VTY_CHECK_PW, req_opts, resp_opts);
    if (ret != VTY_SUCC) return false;
    return true;
}

bool vty_agent_session::change_mode(const std::string& mode)
{
    __u32 ret = VTY_SUCC;
    string_vector req_opts, resp_opts;

    req_opts.push_back(mode);
    ret = send_vty_msg(id(), VTY_CHG_MODE, req_opts, resp_opts);
    if (ret != VTY_SUCC) return false;
    return true;
}

const std::string vty_agent_session::current_mode()
{
    __u32 ret = VTY_SUCC;
    string_vector req_opts, resp_opts;
    std::string tmp;

    ret = send_vty_msg(id(), VTY_CUR_MODE, req_opts, resp_opts);
    PDBG("ret(%d), optnum(%d)\n", ret, resp_opts.size());
    if (ret != VTY_SUCC || resp_opts.empty()) return  tmp;
    return resp_opts[0];
}

int vty_agent_session::output(const char c)
{
    return 0;
};

int vty_agent_session::output(const std::string& string)
{
    __u32 ret = VTY_SUCC;
    string_vector req_opts, resp_opts;
	std::string tmp;
    std::size_t rsize = string.length();
	std::size_t start = 0, end = 0;
	std::size_t lnum = rsize/VTY_MAX_IPC_BODY_SIZE;

	PDBG("size: %d, loop num: %d\n", rsize, lnum);
	for (; ; start += end, rsize -= end) {
		if (rsize > VTY_MAX_IPC_BODY_SIZE) {
			end = VTY_MAX_IPC_BODY_SIZE;
			tmp = string.substr(start, end);
		} else {
			tmp = string.substr(start); 
		}
		req_opts.push_back(tmp);
		ret = send_vty_msg(id(), VTY_OUTPUT, req_opts, resp_opts);
		PDBG("VTY_OUTPUT: RET: %d\n", ret);
		req_opts.clear();
		if (rsize < VTY_MAX_IPC_BODY_SIZE)
			break;
	}
    return 0;
};

int vty_agent_session::output(const char* fmt, ...)
{
    va_list     args;
    std::size_t size = 0, len = 0, max_body = vty_ipc_message_t::max_body_length;
    char buf[8192];  /* FIXME */
    char *ptr = buf;
    std::string tmp;

    va_start (args, fmt);
    len = vsnprintf (buf, sizeof buf, fmt, args);
    va_end (args);

    if (len > 8192) {
        throw std::runtime_error("overflow internal buffer!!!!");
        return -1; /* FIXME: kauri */
    }
	tmp.assign(buf, len);

	return this->output(tmp);
};

bool vty_agent_session::is_dialog() 
{ 
    __u32 ret = VTY_SUCC;
    string_vector req_opts, resp_opts;

    ret = send_vty_msg(id(), VTY_IS_DIAG, req_opts, resp_opts);
    if (ret != VTY_SUCC) return  false;
    return true;
}

int vty_agent_session::stop_dialog() 
{ 
    __u32 ret = VTY_SUCC;
    string_vector req_opts, resp_opts;

    _diag_function = NULL;
    ret = send_vty_msg(id(), VTY_STOP_DIAG, req_opts, resp_opts);
    return 0; 
}

int vty_agent_session::start_dialog(DialogHandle function)
{
    __u32 ret = VTY_SUCC;
    string_vector req_opts, resp_opts;
    vty_ipc_message_t msg;

    if (is_dialog() == true) {
        //stop_dialog();  /* FIXME */
        return -1;
    }

    cmd_agent_mng::instance()->set_dialog(id(), function);

    _ipcmsg.build_msg(id(), VTY_START_DIAG, VTY_RET_NONE, req_opts, msg);
    if (send_ipc(msg) == false) return 0xFF;    /* FIXME */
    return 0;
}

int vty_agent_session::start_function(int vty_id, string_vector& opts)
{
    __u64 function_id; 
    ExcuteHandle function;
    cmd_agent_mng_ptr cmdmng;
	string_vector func_opts;

    function_id = strtoul(opts[0].c_str(), NULL, 16);
    PDBG("VTY ID(%d), FUNC ID(0x%llx)\n", vty_id, function_id);
    if (cmd_agent_mng::instance()->get_function(function_id, function) != true) {
        return true;
    }

    _id = vty_id;
    std::string test("tst string");
	func_opts.insert(func_opts.begin(), opts.begin() + 1, opts.end());
    int ret = function(shared_from_this(), test, func_opts);
    return ret;
}

int vty_agent_session::handle_diag_function(string_vector& opts)
{
    if (cmd_agent_mng::instance()->get_dialog(id(), _diag_function) != true) {
        PDBG("_diag_function is NULL!!!!\n");
        stop_dialog();
        stop();
        return -1;
    }
    PDBG("DIAG-MSG: %s\n", opts[0].c_str());
    _diag_function(shared_from_this(), opts[0]);    /* FIXME */
    return 0;
}

bool vty_agent_session::handle_vty_mesage(vty_ipc_message_t& msg)
{
    int ret = -1;
    __u32 vty_id, cmd, sub;
    string_vector opts, resp_opts;

    _ipcmsg.parse_msg(msg, vty_id, cmd, sub, opts);
    _id = vty_id; /* FIXME */

    PDBG(">> recv cmd: %d\n", cmd);
	if (cmd == VTY_RUN_CFG) {
		cmd_agent_mng::instance()->show_agent_runcfg(shared_from_this());
		ret = 0;
	} else if(cmd == VTY_EXEC_FUNC) {
        ret = start_function(vty_id, opts);
    } else if (cmd == VTY_DIAG_MSG) {
        ret = handle_diag_function(opts);
    } else {
        PDBG(">>>>>>>> wrong msg sequence (cmd:%d)!!!!!!!<<<< \n", cmd);
    }

    if (ret == 0) sub = VTY_SUCC;
    else sub = VTY_FAIL;
    _ipcmsg.build_msg(vty_id, cmd, sub, resp_opts, msg);

    if (cmd == VTY_DIAG_MSG) {
        PDBG("send VTY_DIAG_MSG\n");
    }
    
    return send_ipc(msg);
};

void vty_agent_session::run()
{
    for (;;)
    {
        if (recv_ipc(_recv_msg) == false) break;
        if (handle_vty_mesage(_recv_msg) == false) break;
    }
}
